<?php
	error_reporting(E_ALL ^ E_NOTICE);
	ob_implicit_flush();
	set_time_limit(0);
	$sk= new Sock('127.0.0.1',9527);
	//$sk->e("myserver work");
	$sk->run();
	class Sock
	{
		public $sockets;
		public $users;
		public $master;
		private $sda  = array () ; //已接收的数据
	    private $slen = array () ; //数据总长度
	    private $sjen = array () ; //接收数据的长度
	    private $ar   = array () ; //加密key
	    private $n    = array () ;

		public function __construct($address,$port)
		{
			$this->master = $this->WebSocket($address,$port);
			$this->sockets = array($this->master);
		}
		function WebSocket($address,$port)
		{
			$server = socket_create(AF_INET,SOCK_STREAM, SOL_TCP);
			socket_set_option($server, SOL_SOCKET, SO_REUSEADDR,1);
			socket_bind($server, $address,$port);
			socket_listen($server);
			$this->e('Server Started');
			$this->e('Listening on :'.$address.' port '.$port);
			return $server;
		}
		function run()
		{
			while(true)
			{
				$changes = $this->sockets;
				$write= NULL;
				$except =NULL;
				socket_select($changes, $write, $except, NULL);
				foreach($changes as $sock)
				{
					if ($sock == $this->master)
					{
						$client = socket_accept($this->master);
						$key = uniqid();
						$this->sockets[] = $client;
						$this->users[$key] = array(
								'socket' =>$client,
								'shakehand' =>false,
								'username' =>''
							);
					}
					else
					{
						$len = 0;
						$buffer ='';
						do
						{
							$l = socket_recv($sock, $buf, 1000, 0);
							$len+=$l;
							$buffer.=$buf;
						}
						while($l==1000);
						$userkeyid = $this ->searchuserid($sock);
						if ($len<7)
						{
							$this->logout($userkeyid);
							continue;
						}
						if (!$this->users[$userkeyid]['shakehand'])
						{
							$this->shakehand($userkeyid,$buffer);

						}else
						{
							$buffer = $this->uncode($buffer,$userkeyid);
							if ($buffer==false)
							{
								continue;
							}
							echo $buffer;
							$this->serveracction($buffer,$userkeyid);
						//	$this->send($sock,"this is from server");
						/*trim($buffer);
						echo $buffer;
						echo $len;
						$this->send($sock,"this is send");*/
						}
						
					}
				}
			}
		}
		function serveracction($buffer,$userkeyid)
		{
			$userinfo = json_decode($buffer,true);
			if ($userinfo['type']=='login') 
				{
					$this->login($userinfo['clientname'],$userkeyid);
					$msg=$this->getalluser();
					echo $msg;
					$this->send($this->users[$userkeyid]['socket'],$msg);
					$this->newuseradd($userinfo['clientname']);
				}
			if ($userinfo['type']=='msg'){
				if ($userinfo['touser']=='all')
				{
					$this->broadcastall($userinfo['content'],$userinfo['clientname']);
				}else
				{
					$touserkeyid=$this->locatuser($userinfo['touser']);
					if ($touserkeyid==false)
					{
						//$msg='{fromuser:\'server\',content:\'user are not found\',state:1}';
						$msg = array(
								'fromuser'=>"server",
								'content'=>"user are not found",
								'state'=>5
												);
						$msg=json_encode($msg);
						$this->send($this->users[$userkeyid]['socket'],$msg);
			
					}else{
					//	$msg='{fromuser:\''.$userinfo['clientname'].'\'content:\''.$userinfo['content'].'\',state:1}';
						$msg = array(
								'fromuser'=>$userinfo['clientname'],
								'content'=>$userinfo['content'],
								'state'=>4
												);
						$msg=json_encode($msg);
						$this->send($this->users[$touserkeyid]['socket'],$msg);
					}
				}
			}
			if ($userinfo['type']=='logout')
			{
				//$msg='{"fromuser","server":"content":"user1:user2:user3","state": 1}';	
			
				//$msg=$this->getalluser();
				
				$this->logout($userkeyid);
				
			}
			var_dump($userinfo);
			//echo $userinfo['type'];
			//echo $userinfo['touser'];
		}
		/**
		 * 通过用户名找到要私聊对象的uniquekey
		 *return 	返回的私聊对象的uniquekey,如果没找打返回false
		 */
		function locatuser($tousername)
		{
			foreach ($this->users as $key => $value) 
			{
				if ($value['username']==$tousername) return $key;
			}
			return false;
		}
		function newuseradd($clientname)
		{	
		//	$msg='{fromuser:\''.$clientname.' to everyone\'content:\''.$content.'\',state:1}';
			$msg = array(
				'fromuser'=>$clientname,
				'content'=>"user ".$clientname." join us",
				'state'=>7
								);
			$msg=json_encode($msg);
			foreach ($this->users as $key => $value) 
			{
				$this->send($value['socket'],$msg);
			}
		}
		function broadcastall($content,$clientname)
		{	
		//	$msg='{fromuser:\''.$clientname.' to everyone\'content:\''.$content.'\',state:1}';
			$msg = array(
				'fromuser'=>$clientname,
				'content'=>$content,
				'state'=>6
								);
			$msg=json_encode($msg);
			foreach ($this->users as $key => $value) 
			{
				$this->send($value['socket'],$msg);
			}
		}
		function login($newusername,$userkeyid)
		{
		//	array_push($this->users[$userkeyid],array('username' => $newusername ));
			$this->users[$userkeyid]['username']=$newusername;
			//var_dump($this->users[$userkeyid]);
		}

		function logout($userkeyid)
		{
			$msg = array(
				'fromuser'=>$this->users[$userkeyid]['username'],
				'content'=>$this->users[$userkeyid]['username'],
				'state'=>3
								);
			$msg=json_encode($msg);	
			socket_close($this->users[$userkeyid]['socket']);
			$this->e('user '.$this->users[$userkeyid]['username'].' logout');
			unset($this->users[$userkeyid]);
			$this->sockets = array($this->master);
			foreach ($this->users as 
							 $value)
			{
					$this->sockets[] = $value['socket'];				
			}
			foreach ($this->users as $key => $value) 
				{
					$this->send($value['socket'],$msg);
				}
			$this->e("key: ".$userkeyid." close");
		}
		function getalluser()
		{
		
			// $clientjson='{fromuser:\'server\',content:\'';
			foreach ($this->users as $key => $value) 
				 {
				 $clientjsonmsg.=$value['username'];
				 $clientjsonmsg.='|';
				 }
			// $clientjson.='\',state:\'1\'}';	
				$clientjson = array(
				'fromuser'=>'server',
				'content'=>$clientjsonmsg,
				'state'=>1
								);
			return json_encode($clientjson);
		}
		/**
		 * return 找到了则返回那个唯一的用户ID，如果没有找到返回false;
		 */
		function searchuserid($sock)
		{
			foreach ($this->users as
						 $k=> 
						 $v)
			{
				if ($sock==$v['socket'])
					return $k;
			}
			return false;
		}
		 function shakehand ( $userkeyid , $buffer )
    	{
		        $buf = substr ( $buffer , strpos ( $buffer , 'Sec-WebSocket-Key:' ) + 18 ) ;
		        $key = trim ( substr ( $buf , 0 , strpos ( $buf , "\r\n" ) ) ) ;

		        $new_key = base64_encode ( sha1 ( $key . "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" ,
		                        true ) ) ;

		        $new_message = "HTTP/1.1 101 Switching Protocols\r\n" ;
		        $new_message .= "Upgrade: websocket\r\n" ;
		        $new_message .= "Sec-WebSocket-Version: 13\r\n" ;
		        $new_message .= "Connection: Upgrade\r\n" ;
		        $new_message .= "Sec-WebSocket-Accept: " . $new_key . "\r\n\r\n" ;

		        socket_write ($this->users[$userkeyid]['socket'] , $new_message ,
		                strlen ( $new_message ) ) ;
		        $this->users[ $userkeyid ][ 'shakehand' ] = true ;
    	    return true ;
  		}
  		function send($socket,$msg)
  		{
  			$msg=$msg.'\r\n';
  			$msg = $this->code($msg);
  			socket_write($socket,$msg,strlen($msg));
  		}
  	function uncode ( $str , $key )
    {
        $mask = array () ;
        $data = '' ;
        $msg  = unpack ( 'H*' , $str ) ;
        $head = substr ( $msg[ 1 ] , 0 , 2 ) ;
        if ( $head == '81' && ! isset ( $this->slen[ $key ] ) )
        {
            $len = substr ( $msg[ 1 ] , 2 , 2 ) ;
            $len = hexdec ( $len ) ;
            if ( substr ( $msg[ 1 ] , 2 , 2 ) == 'fe' )
            {
                $len      = substr ( $msg[ 1 ] , 4 , 4 ) ;
                $len      = hexdec ( $len ) ;
                $msg[ 1 ] = substr ( $msg[ 1 ] , 4 ) ;
            }
            else if ( substr ( $msg[ 1 ] , 2 , 2 ) == 'ff' )
            {
                $len      = substr ( $msg[ 1 ] , 4 , 16 ) ;
                $len      = hexdec ( $len ) ;
                $msg[ 1 ] = substr ( $msg[ 1 ] , 16 ) ;
            }
            $mask[] = hexdec ( substr ( $msg[ 1 ] , 4 , 2 ) ) ;
            $mask[] = hexdec ( substr ( $msg[ 1 ] , 6 , 2 ) ) ;
            $mask[] = hexdec ( substr ( $msg[ 1 ] , 8 , 2 ) ) ;
            $mask[] = hexdec ( substr ( $msg[ 1 ] , 10 , 2 ) ) ;
            $s      = 12 ;
            $n      = 0 ;
        }
        else if ( $this->slen[ $key ] > 0 )
        {
            $len  = $this->slen[ $key ] ;
            $mask = $this->ar[ $key ] ;
            $n    = $this->n[ $key ] ;
            $s    = 0 ;
        }

        $e = strlen ( $msg[ 1 ] ) - 2 ;
        for ( $i = $s ;
                $i <= $e ;
                $i+= 2 )
        {
            $data .= chr ( $mask[ $n % 4 ] ^ hexdec ( substr ( $msg[ 1 ] , $i ,
                                    2 ) ) ) ;
            $n ++ ;
        }
        $dlen = strlen ( $data ) ;

        if ( $len > 255 && $len > $dlen + intval ( $this->sjen[ $key ] ) )
        {
            $this->ar[ $key ]   = $mask ;
            $this->slen[ $key ] = $len ;
            $this->sjen[ $key ] = $dlen + intval ( $this->sjen[ $key ] ) ;
            $this->sda[ $key ]  = $this->sda[ $key ] . $data ;
            $this->n[ $key ]    = $n ;
            return false ;
        }
        else
        {
            unset ( $this->ar[ $key ] , $this->slen[ $key ] ,
                    $this->sjen[ $key ] , $this->n[ $key ] ) ;
            $data = $this->sda[ $key ] . $data ;
            unset ( $this->sda[ $key ] ) ;
            return $data ;
        }
    }

    function code ( $msg )
    {
        $frame      = array () ;
        $frame[ 0 ] = '81' ;
        $len        = strlen ( $msg ) ;
        if ( $len < 126 )
        {
            $frame[ 1 ] = $len < 16 ? '0' . dechex ( $len ) : dechex ( $len ) ;
        }
        else if ( $len < 65025 )
        {
            $s          = dechex ( $len ) ;
            $frame[ 1 ] = '7e' . str_repeat ( '0' , 4 - strlen ( $s ) ) . $s ;
        }
        else
        {
            $s          = dechex ( $len ) ;
            $frame[ 1 ] = '7f' . str_repeat ( '0' , 16 - strlen ( $s ) ) . $s ;
        }
        $frame[ 2 ] = $this->ord_hex ( $msg ) ;
        $data       = implode ( '' , $frame ) ;
        return pack ( "H*" , $data ) ;
    }

    function ord_hex ( $data )
    {
        $msg = '' ;
        $l   = strlen ( $data ) ;
        for ( $i = 0 ;
                $i < $l ;
                $i ++ )
        {
            $msg .= dechex ( ord ( $data{$i} ) ) ;
        }
        return $msg ;
    }
    	/*function close($userkeyid)
    	{
    		socket_close($this->u)
    	}*/
		function e ($str)
		{
			$str = $str."\n";
			echo iconv('utf-8','gbk//IGNORE',$str);
		}
	}
?>